/*
 * @Author: Mainul Islam
 */

package edu.uta.cse.randomgraph.graph;

import java.awt.Color;
import java.awt.Graphics;
import java.text.DecimalFormat;

import edu.uta.cse.randomgraph.graph.property.DeterministicNodeSet;
import edu.uta.cse.randomgraph.util.DirUtil;
import edu.uta.cse.randomgraph.util.LogType;
import edu.uta.cse.randomgraph.util.LogWriter;
import edu.uta.cse.randomgraph.util.MyDrawUtil;


public class ScaleFreeDeterministicGraph extends MyGraph {
	
	private static String folderName = "Deterministic";

	private int _n, _k;
	private int currNumNodes;
	private DeterministicNodeSet nodeSet[];
	private static int unitWidthSpace = 20;
	private static int unitHeightSpace = 8;
	
	public ScaleFreeDeterministicGraph(int k, int maxNumNode) {
		super( (int)Math.pow( (double)(k+1), (double)Math.floor((Math.log(maxNumNode)/Math.log(k+1))) ) );
		_n = (int)(Math.log(maxNumNode)/Math.log(k+1));
		_k = k;
		currNumNodes = (int)Math.pow(_k+1, _n);
		nodeSet = new DeterministicNodeSet[_n+1];
		initGraph();
	}	
		
	private void initGraph() {
		numNodes = currNumNodes;
		prevView = "Structured";
	}
	
	private void initPreCompute() {
		initDist();
	}

	private void preCompute() {
		computeFloydWarshal();
		computeDegreeList();
	}

	public void generateGraph() {
		initPreCompute();
		
		generateDeterministicNodes();
		setMyGraph(true);
		
		preCompute();
	}

	private void generateDeterministicNodes () {
		
		nodeSet[0] = new DeterministicNodeSet(_k, 0);
		nodeSet[0].initRootNode();
		
		for(int i=1; i<=_n; i++) {
			nodeSet[i] = new DeterministicNodeSet(_k, i);
			nodeSet[i].generateNodes(nodeSet[i-1]);
		}
	}
	
	private void setMyGraph(boolean updateDistance) {
		int i, j;
		for(i=0; i<nodeSet[_n]._numNodes; i++) {
			nodes[i] = nodeSet[_n]._nodes[i];
		}

		this.numEdges = nodeSet[_n]._numEdges;
		for(i=0; i<nodeSet[_n]._numNodes; i++) {
			for(j=0; j<nodeSet[_n]._numNodes; j++) {
				if(nodeSet[_n]._edgeMat[i][j] == true) {
					this.edgeMat[i][j] = true;
					if( updateDistance )
						dist[i][j] = dist[j][i] = 1;
				}
			}
		}
	}
	
	private void generateStructuredCoOrdinates(int currWidth, int currHeight) {
		nodeSet[0].initRootNodePosition(currWidth/2, 25);
		unitHeightSpace = (currHeight - 50) / (int)Math.pow(2, _n);
		for(int i=1; i<=_n; i++) {
			nodeSet[i].generateNodeCoordinates(nodeSet[i-1], unitWidthSpace, unitHeightSpace);
		}
	}

	
	public void drawGraph(int currWidth, int currHeight, Graphics g) {

			if(this.drawStructuredView) {
				generateStructuredCoOrdinates(currWidth, currHeight);
				setMyGraph(false);
				prevView = "Structured";
			}
			else {
				if(isHeightWidthChanged(currWidth, currHeight) || prevView.equals("Structured")) {
					MyDrawUtil.generateCoordinatesForNodes(currWidth, currHeight, nodes, numNodes);
					prevView = "Random";
				}
			}
		
		computeDegreeList();
		int maxDegVal = Integer.MAX_VALUE;
		if(this.degreeValueList.size() > 0) 
			maxDegVal = this.degreeValueList.get( this.degreeValueList.size()-1 );
		           
		Color c;
		
		for (int i=0; i<numNodes; i++)	{
			if(nodes[i].deg >= maxDegVal && maxDegVal > 0)
				MyDrawUtil.drawNode(nodes[i].X, nodes[i].Y, nodeDiam+4, Color.GREEN, g);
			else
				MyDrawUtil.drawNode(nodes[i].X, nodes[i].Y, nodeDiam, Color.RED, g);
		}
		
		
		for (int i=0; i<numNodes-1; i++) {
			for (int j=i+1; j<numNodes; j++) {
				if ( edgeMat[i][j] ) {
					if( (nodes[i].deg >= maxDegVal || nodes[j].deg >= maxDegVal) && maxDegVal > 0)
						c = Color.BLACK;
					else 
						c = Color.LIGHT_GRAY;					
					MyDrawUtil.drawEdge(nodes[i], nodes[j], nodeRad, c, g);
				}
			}
		}
	}
	
	public void writeInfo(LogWriter lw, LogType lt) {
		String dirName, fileName;
		
		dirName = lw.outputDirectory + folderName;
		if( !lw.createDirectory(dirName) )
			return;
		
		fileName = dirName + "/" + lw.getLogTypeFileName(lt);		

		if(lt == LogType.DegreeDistribution || lt == LogType.AgeDegreeRelation) {
				switch(lt) {
					case AgeDegreeRelation:		
						dirName  = dirName + "/" + lw.ageDegreeRelationFolderName;		break;
					case DegreeDistribution:
						dirName  = dirName + "/" + lw.degDistFolderName;		break;
				}
				
				if( !lw.createDirectory(dirName) )
					return;
				int ind = DirUtil.getLastFileNameIndexInDirectory(dirName);
				fileName = dirName + "/" + Integer.toString(ind+1) + ".txt";
		}

		if(!lw.openWriter(fileName))
			return;

		switch(lt) {
			case Summary:				writeSummary(lw);				break;
			case AgeDegreeRelation:		writeAgeDegreeRelation(lw);		break;
			case DegreeDistribution:	writeDegreeDistribution(lw);	break;
		}
		
		lw.closeWriter();
	}

	private void writeSummary(LogWriter lw) {
		DecimalFormat df = new DecimalFormat("#.########");
		
		double avgDeg = 0.0;
		if (numNodes != 0) 
			avgDeg = (double)(numEdges*2)/(double)numNodes;
		
		lw.write( String.format("%10s", numNodes) );
		lw.write( String.format("%10s", numEdges) );
		lw.write( String.format("%10s", diameter) );
		lw.write( String.format("%10s", numConComp) );
		lw.write( String.format("%10s", maxSizeConComp) );
		lw.write( String.format("%16s", df.format(avgClusCo)) );
		lw.write( String.format("%16s", df.format(avgDeg)) );
		lw.writeLine();
	}

}
